const media = new Audio();
const context = new AudioContext();

const trackNode = context.createMediaElementSource(media);
const allGainNode = context.createGain();
const	leftGainNode = context.createGain();
const	rightGainNode = context.createGain();
const	analyserNode = context.createAnalyser();
const	bfLowNode = context.createBiquadFilter();
const	bfPeakingNode = context.createBiquadFilter();
const	bfHighNode = context.createBiquadFilter();
const	spliterNode = context.createChannelSplitter(2);
const	mergerNode = context.createChannelMerger(2);
const	panner2DNode = context.createStereoPanner();
const	panner3DNode = context.createPanner();

context.receiver = allGainNode;

let frequencyU8A, timeDomainU8A;

const defaultConfig = {
	allGain: 1,
	leftGain: 1,
	rightGain: 1,
	bfLowGain: 0,
	bfLowFreq: 320,
	bfPeakingGain: 0,
	bfPeakingQ: 1,
	bfPeakingFreq: 1000,
	bfHighGain: 0,
	bfHighFreq: 3200,
	pannerMode: '2D',
	panner2D: 0,
	panner3DX: 0,
	panner3DY: 0,
	panner3DZ: 0,
	panner3DMaxDt: 1000,
	panner3DRefDt: 50,
	panner3DRof: 1,
	analyFFTSize: 1024
}

function connectNodes(config) {
	const mergerdConfig = { ...defaultConfig, config };
	return context.resume().then(() => {
		allGainNode.gain.value = mergerdConfig.allGain;
		leftGainNode.gain.value = mergerdConfig.leftGain;
		rightGainNode.gain.value = mergerdConfig.rightGain;
		analyserNode.fftSize = mergerdConfig.analyFFTSize;
		bfLowNode.type = 'lowshelf';
		bfLowNode.frequency.value = mergerdConfig.bfLowFreq;
		bfLowNode.gain.value = mergerdConfig.bfLowGain;
		bfPeakingNode.type = 'peaking';
		bfPeakingNode.frequency.value = mergerdConfig.bfPeakingFreq;
		bfPeakingNode.Q.value = mergerdConfig.bfPeakingQ;
		bfPeakingNode.gain.value = mergerdConfig.bfPeakingGain;
		bfHighNode.type = 'highshelf';
		bfHighNode.frequency.value = mergerdConfig.bfHighFreq;
		bfHighNode.gain.value = mergerdConfig.bfHighGain;
		panner2DNode.pan.value = mergerdConfig.panner2D;
		panner3DNode.panningModel = 'HRTF';
		panner3DNode.distanceModel = 'inverse';
		panner3DNode.maxDistance = mergerdConfig.panner3DMaxDt;
		panner3DNode.refDistance = mergerdConfig.panner3DRefDt;
		panner3DNode.rolloffFactor = mergerdConfig.panner3DRof;
		panner3DNode.coneInnerAngle = 360;
		panner3DNode.orientationX.value = 0;
		panner3DNode.orientationY.value = 0;
		panner3DNode.orientationZ.value = 1;
		panner3DNode.positionX.value = mergerdConfig.panner3DX;
		panner3DNode.positionY.value = mergerdConfig.panner3DY;
		panner3DNode.positionZ.value = mergerdConfig.panner3DZ;
		context.listener.forwardX.value = 0;
		context.listener.forwardY.value = 0;
		context.listener.forwardZ.value = -1;
		context.listener.upX.value = 0;
		context.listener.upY.value = 1;
		context.listener.upZ.value = 0;
		context.listener.positionX.value = 0;
		context.listener.positionY.value = 0;
		context.listener.positionZ.value = 0;
		trackNode.connect(allGainNode);
		allGainNode.connect(spliterNode);
		spliterNode.connect(leftGainNode, 0);
		spliterNode.connect(rightGainNode, 1);
		leftGainNode.connect(mergerNode, 0, 0);
		rightGainNode.connect(mergerNode, 0, 1);
		mergerNode.connect(bfHighNode);
		bfHighNode.connect(bfPeakingNode);
		bfPeakingNode.connect(bfLowNode);
		bfLowNode.connect(panner3DNode);
		bfLowNode.connect(panner2DNode);
		if (mergerdConfig.pannerMode === '3D') {
			panner3DNode.connect(analyserNode);
		} else {
			panner2DNode.connect(analyserNode);
		}
		analyserNode.connect(context.destination);
		frequencyU8A = new Uint8Array(mergerdConfig.analyFFTSize / 2);
		timeDomainU8A = new Uint8Array(mergerdConfig.analyFFTSize);
	})
}

export {
	media,
	context,
	trackNode,
	allGainNode, 
	leftGainNode, 
	rightGainNode,
	analyserNode,
	bfLowNode,
	bfPeakingNode,
	bfHighNode,
	spliterNode,
	mergerNode,
	panner2DNode,
	panner3DNode,
	frequencyU8A, timeDomainU8A,
	connectNodes,
	defaultConfig
}